#pragma once
#include <queue>
#include <pthread.h>

namespace bq
{
    const int DEFAULT_CAP = 5;
    template <typename T>
    class BlockQueue
    {
    public:
        BlockQueue(int cap = DEFAULT_CAP)
            : _capacity(cap)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_consumer_cond, nullptr);
            pthread_cond_init(&_produtor_cond, nullptr);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_consumer_cond);
            pthread_cond_destroy(&_produtor_cond);
        }
        BlockQueue(const BlockQueue<T> &) = delete;
        BlockQueue<T>& operator=(const BlockQueue<T> &) = delete;

        // 生产
        void push(const T &in)
        {
            pthread_mutex_lock(&_mtx);

            // if (queueIsFull()) // 在临界区内进行条件判断。
            while (queueIsFull()) // 在临界区内进行条件判断。  使用while的原因: 可能被误唤醒导致生产多了，比如对方使用了broadcast
            {
                // 进入条件变量，等待唤醒
                // 调用 pthread_cond_wait 的动作：
                //      1. 释放锁。(因此得传个锁过去)
                //      2. 将当前线程挂起(在系统角度，就是切换，会做上下文保护)
                pthread_cond_wait(&_produtor_cond, &_mtx);

                // 因为有上下文保护，被唤醒后从 pthread_cond_wait 函数内继续执行
                // 继续加锁后从 pthread_cond_wait 函数返回，继续向后运行。
            }

            _q.push(in);

            // 唤醒消费者
            pthread_cond_signal(&_consumer_cond);

            pthread_mutex_unlock(&_mtx);
        }

        // 消费
        void pop(T &out)
        {
            pthread_mutex_lock(&_mtx);
            // if (queueIsEmpty()) // 超市没货了，消费者就回去等待
            while (queueIsEmpty()) // 超市没货了，消费者就回去等待
                pthread_cond_wait(&_consumer_cond, &_mtx);
            out = _q.front();
            _q.pop();
            // 唤醒生产者
            pthread_cond_signal(&_produtor_cond);
            pthread_mutex_unlock(&_mtx);
        }

    private:
        bool queueIsFull() { return _capacity == _q.size(); }
        bool queueIsEmpty() { return _q.empty(); }

    private:
        std::queue<T> _q;
        int _capacity;                 // 阻塞队列的上限
        pthread_mutex_t _mtx;          // 维护生产者-生产者，生产者-消费者，消费者-消费者的互斥关系
        pthread_cond_t _consumer_cond; // 维护生产者-消费者的同步关系
        pthread_cond_t _produtor_cond; // 维护生产者-消费者的同步关系
    };
}